<!doctype html>
<html class="theme-next use-motion theme-next-mist">
<head>
  
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

  <meta name="google-site-verification" content="K8DCBviaoTBKVs28YBB7IBIbospQ9RVlgSh81RYMUhY" />


  <meta name="baidu-site-verification" content="tXr3ZTm3Hx" />



  <link rel="stylesheet" type="text/css" href="/vendors/fancybox/source/jquery.fancybox.css?v=2.1.5"/>


<link rel="stylesheet" type="text/css" href="/css/main.css?v=0.4.5.1"/>

  <meta name="description" content="xgfe's blog. 鲜果前端的技术博客，鲜果前端研发部官方博客。前端基础技术研究：html, html5, javascript, css, css3；前端框架研究：angularJs, react, react native." />


  <meta name="keywords" content="javascript,web api," />


  <link rel="alternate" target="_blank" href="/atom.xml" title="xgfe" type="application/atom+xml" />


  <link rel="shorticon icon" type="image/x-icon" href="http://p0.meituan.net/xgfe/2db359f56ce13be30dedef160e0e57ce16958.ico?v=0.4.5.1" />

<meta name="description" content="Web Workers 是一种不在页面线程而是在后台线程中执行脚本的技术。Web Workers 的优点在于可以将计算密集型和I/O密集型的操作放在一个单独的后台线程中执行，进而使得主线程（通常是UI线程）能够不被阻塞，也不会被减缓。 本博客主要内容分为七部分：  Web Worker 概念及使用 Web worker的生命周期和处理模型 错误处理 数据传输 线程安全 内容安全策略">
<meta name="keywords" content="javascript,web api">
<meta property="og:type" content="article">
<meta property="og:title" content="web worker详解">
<meta property="og:url" content="http://xgfe.github.io/2017/05/03/LexHuang/web-worker/index.html">
<meta property="og:site_name" content="xgfe">
<meta property="og:description" content="Web Workers 是一种不在页面线程而是在后台线程中执行脚本的技术。Web Workers 的优点在于可以将计算密集型和I/O密集型的操作放在一个单独的后台线程中执行，进而使得主线程（通常是UI线程）能够不被阻塞，也不会被减缓。 本博客主要内容分为七部分：  Web Worker 概念及使用 Web worker的生命周期和处理模型 错误处理 数据传输 线程安全 内容安全策略">
<meta property="og:image" content="http://p0.meituan.net/kuailv/678b764c4d0bff4629f20e748357481c29564.jpg">
<meta property="og:image" content="http://p1.meituan.net/kuailv/1aae5f5a2fd5220d14bf377ffd82937840137.png">
<meta property="og:image" content="http://p0.meituan.net/kuailv/2eeab3203902eee3bf8565f7fbedaace198201.png">
<meta property="og:updated_time" content="2017-07-06T09:59:23.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="web worker详解">
<meta name="twitter:description" content="Web Workers 是一种不在页面线程而是在后台线程中执行脚本的技术。Web Workers 的优点在于可以将计算密集型和I/O密集型的操作放在一个单独的后台线程中执行，进而使得主线程（通常是UI线程）能够不被阻塞，也不会被减缓。 本博客主要内容分为七部分：  Web Worker 概念及使用 Web worker的生命周期和处理模型 错误处理 数据传输 线程安全 内容安全策略">
<meta name="twitter:image" content="http://p0.meituan.net/kuailv/678b764c4d0bff4629f20e748357481c29564.jpg">


<script type="text/javascript" id="hexo.configuration">
  var CONFIG = {
    scheme: 'Mist',
    sidebar: 'post'
  };
</script>

  <title> web worker详解 | xgfe </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
  <div style="position: fixed; top: -9999px; left: -9999px;">
    <img src="http://p0.meituan.net/xgfe/082a9624ba5ae8602150a2d43968463e49348.png" alt="xgfe"/>
  </div>
  <!--[if lte IE 8]>
  <div style=' clear: both; height: 59px; padding:0 0 0 15px; position: relative;margin:0 auto;'>
    <a href="http://windows.microsoft.com/en-US/internet-explorer/products/ie/home?ocid=ie6_countdown_bannercode">
      <img src="http://7u2nvr.com1.z0.glb.clouddn.com/picouterie.jpg" border="0" height="42" width="820"
           alt="You are using an outdated browser. For a faster, safer browsing experience, upgrade for free today or use other browser ,like chrome firefox safari."
           style='margin-left:auto;margin-right:auto;display: block;'/>
    </a>
  </div>
<![endif]-->
  

  <script type="text/javascript">
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "//hm.baidu.com/hm.js?3601d4483819a5ab6ddabb0b6422a328";
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(hm, s);
    })();
  </script>



  <div class="container one-column page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><h1 class="site-meta">
  <span class="logo-line-before"><i></i></span>
  <a href="/" class="brand" rel="start">
      <span class="logo">
        <i class="icon-next-logo"></i>
      </span>
      <span class="site-title">xgfe</span>
  </a>
  <span class="logo-line-after"><i></i></span>
</h1>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
  
  

  
    <ul id="menu" class="menu menu-left">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            <i class="menu-item-icon icon-next-home"></i> <br />
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">
            <i class="menu-item-icon icon-next-archives"></i> <br />
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">
            <i class="menu-item-icon icon-next-tags"></i> <br />
            标签
          </a>
        </li>
      
        
        <li class="menu-item menu-item-join">
          <a href="/join" rel="section">
            <i class="menu-item-icon icon-next-join"></i> <br />
            加入我们
          </a>
        </li>
      
      <!-- slide-links added by felix -->
      <li class="menu-item menu-item-slides" style="opacity: 1; transform: translateY(0px);">
        <a href="http://xgfe.github.io/Basics/" target="_blank" rel="section">
          <i class="menu-item-icon icon-next-slides"></i> <br>
          Basics
        </a>
      </li>
      <li class="menu-item menu-item-slides" style="opacity: 1; transform: translateY(0px);">
        <a href="https://slides.com/xgfe" target="_blank" rel="section">
          <i class="menu-item-icon icon-next-slides"></i> <br>
          Slides
        </a>
      </li>

      
      
    </ul>
  

  
    <div class="site-search">
      

    </div>
  

    <div class="site-search">
      <form class="site-search-form" id="gg-form" action="https://www.google.com/webhp" >
        <input type="text" name="q" id="gg-search-input" class="menu-search-input">
      </form>
    </div>
</nav>
 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div id="content" class="content"> 

  <div id="posts" class="posts-expand">
    

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
    <header class="post-header">

      
      
        <h1 class="post-title" itemprop="name headline">
          
          
            
              web worker详解
            
          
        </h1>
      

      <div class="post-meta">
        <span class="post-time">
          发表于
          <time itemprop="dateCreated" datetime="2017-05-03T11:30:00+08:00" content="2017-05-03">
            2017-05-03
          </time>
        </span>

        
          <span class="post-category" >
            &nbsp; | &nbsp; 作者
            
              <span itemprop="about" itemscope itemtype="https://schema.org/Thing">
                <a href="/categories/LexHuang/" itemprop="url" rel="index">
                  <span itemprop="name">LexHuang</span>
                </a>
              </span>

              
              

            
          </span>
        

        
          
        

        <!-- tags 挪动位置 -->
        
          <span class="post-tags">
            &nbsp; | &nbsp;
            
              <a href="/tags/javascript/" rel="tag"><i class="icon-next-tags"></i>javascript</a>
            
              <a href="/tags/web-api/" rel="tag"><i class="icon-next-tags"></i>web api</a>
            
          </span>
        
      </div>
    </header>

    <div class="post-body">

      
      

      
        <span itemprop="articleBody"><p><img src="http://p0.meituan.net/kuailv/678b764c4d0bff4629f20e748357481c29564.jpg" alt="" title="web worker"><br>Web Workers 是一种不在页面线程而是在后台线程中执行脚本的技术。Web Workers 的优点在于可以将计算密集型和I/O密集型的操作放在一个单独的后台线程中执行，进而使得主线程（通常是UI线程）能够不被阻塞，也不会被减缓。</p>
<p>本博客主要内容分为七部分：</p>
<ul>
<li>Web Worker 概念及使用</li>
<li>Web worker的生命周期和处理模型</li>
<li>错误处理</li>
<li>数据传输</li>
<li>线程安全</li>
<li>内容安全策略</li>
</ul>
<a id="more"></a>
<h2 id="Web-Workers-概念"><a href="#Web-Workers-概念" class="headerlink" title="Web Workers 概念"></a>Web Workers 概念</h2><p>所谓的 worker 就是用一个构造器创建的（例如<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Worker/Worker" target="_blank" rel="external">Worker()</a>） ，用来运行一个 JavaScript 文件的函数 — 这个文件包含了将要在 worker 线程中执行的代码<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">worker = <span class="keyword">new</span> Worker(scriptURL [, options ])</div></pre></td></tr></table></figure></p>
<p><a href="https://html.spec.whatwg.org/multipage/workers.html#dedicated-workers-and-the-worker-interface" target="_blank" rel="external">options</a>可以被用来确保worker的全局环境是否支持Javascript模块{type:’module’}，默认为’class’。在共享worker中还可以通过指定<strong>credentials</strong>来制定<strong>scriptURL</strong>的被获取方式。</p>
<p>worker 脚本将在一个与当前 window 对象不同的全局上下文环境中运行，根据检测到的 worker 的类型有所不同,会生成不同的上下文对象。在<code>专属worker</code>(Dedicated Worker)中这个上下文环境由一个叫做 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/DedicatedWorkerGlobalScope" target="_blank" rel="external">DedicatedWorkerGlobalScope</a> 的对象来表示。在<code>共享worker</code>（Shared Worker）则会使用<a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorkerGlobalScope" target="_blank" rel="external">SharedWorkerGlobalScope</a>。所有上下文对象都继承于<a href="https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope" target="_blank" rel="external">WorkerGlobalScope</a></p>
<p>你可以在你的 worker 线程中运行任意的javascript代码，除某些特殊情况外。比如，你不能直接在 worker 线程中操纵 DOM 元素, 或者使用某些 window 对象中默认的方法和属性。 但是 window 对象中很多的方法和属性你是可以使用的，包括 WebSockets，以及诸如 IndexedDB 和 FireFox OS 中独有的 Data Store API 这一类数据存储机制。具体的可用api和函数列表如下：</p>
<ul>
<li>对比不同类型的workers所拥有的属性和类型:</li>
</ul>
<table>
<thead>
<tr>
<th>function</th>
<th>Dedicated workers</th>
<th>Shared workers</th>
</tr>
</thead>
<tbody>
<tr>
<td>atob()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>btoa()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>clearInterval()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>clearTimeout()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>dump()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>setInterval()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>setTimeout()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>importScripts()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>close()</td>
<td>yes, on WorkerGlobalScope</td>
<td>yes, on WorkerGlobalScope</td>
</tr>
<tr>
<td>postMessage()</td>
<td>yes, on DedicatedWorkerGlobalScope</td>
<td>no</td>
</tr>
</tbody>
</table>
<ul>
<li>worker中可用的web API：<ul>
<li>Channel Messaging API</li>
<li>Console API</li>
<li>CustomEvent</li>
<li>DOMRequest and DOMCursor</li>
<li>FileReader</li>
<li>FileReaderSync</li>
<li>FormData</li>
<li>ImageData</li>
<li>IndexedDB</li>
<li>Notifications</li>
<li>Performance</li>
<li>Promise</li>
<li>Worker</li>
<li>WorkerGlobalScope</li>
<li>WorkerLocation</li>
<li>WorkerNavigator</li>
<li>XMLHttpReques</li>
</ul>
</li>
</ul>
<p>可见，web worker可以执行所有常见的I/O操作。</p>
<hr>
<p>以上api去除了实验阶段api和非w3c标准api。</p>
<hr>
<p> 在专属worker里，主线程和 worker 线程之间通过消息机制来互相传输信息：两端都使用 postMessage() 方法来发送信息, 并且通过 onmessage 这个事件处理函数来接收信息。 （传递的信息包含在 Message 这个事件的数据属性内) 。数据的交互是通过传递副本，而不是直接共享数据。</p>
<p> 共享worker［Shared workers ］指的是那些可以被多个运行在不同窗口的脚本所共享的worker，比如IFrames等，只要这些窗口和该共享worder同源。该类worker比专属workers更复杂些————脚本们利用处于活动中的端口与主线程们进行通信。</p>
<hr>
<p> 本质上专属worker也是通过端口来与主线程进行通信，但是由于双方端口是一致的，因此将此特性隐藏了。</p>
<hr>
<p> 一个 worker 也可以生成新的 worker，只要这些 worker 和它们父页面的宿主同源。  此外，worker 可以通过 XMLHttpRequest 来进行网络通信，只是 XMLHttpRequest 的 responseXML 和 channel 这两个属性的值将总是 null 。</p>
<h2 id="使用web-worker"><a href="#使用web-worker" class="headerlink" title="使用web worker"></a>使用web worker</h2><hr>
<p>随着多核计算机逐渐普及，将复杂计算的任务拆解成多个子任务分配给worker是一个非常有效的办法，这样可以在多处理器内核上运算这些任务。</p>
<hr>
<h3 id="Worker-特征检测"><a href="#Worker-特征检测" class="headerlink" title="Worker 特征检测"></a>Worker 特征检测</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (<span class="built_in">window</span>.Worker) &#123;</div><div class="line">...</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="专属worker"><a href="#专属worker" class="headerlink" title="专属worker"></a>专属worker</h3><p>以下以MDN的<a href="https://mdn.github.io/simple-web-worker/" target="_blank" rel="external">Basic dedicated worker example</a>为例。它允许你输入两个数字相乘。两个乘数被送到一个专属worker中，运算得到乘积后，在返回到原页面并展示出来。</p>
<h4 id="生成一个专属worker"><a href="#生成一个专属worker" class="headerlink" title="生成一个专属worker"></a>生成一个专属worker</h4><p>创建一个新的 <strong>专属worker</strong> 很简单。你只需要调用 Worker() 构造器, 并指定一个worker线程需要执行的脚本的URI:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**main.js**/</span></div><div class="line"><span class="keyword">var</span> myWorker = <span class="keyword">new</span> Worker(<span class="string">"worker.js"</span>);</div></pre></td></tr></table></figure></p>
<h4 id="往dedicated-worker里发送消息以及从dedicated-worker发出消息"><a href="#往dedicated-worker里发送消息以及从dedicated-worker发出消息" class="headerlink" title="往dedicated worker里发送消息以及从dedicated worker发出消息"></a>往dedicated worker里发送消息以及从dedicated worker发出消息</h4><p>worker的奇妙之处在于 postMessage()方法和onmessage 事件处理程序。当你想要传递一个消息到worker,你需要像这样传递消息:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**main.js**/</span></div><div class="line">first.onchange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  myWorker.postMessage([first.value,second.value]);</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Message posted to worker'</span>);</div><div class="line">&#125;</div><div class="line">second.onchange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  myWorker.postMessage([first.value,second.value]);</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Message posted to worker'</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>这里我们有两个<code>&lt;input&gt;</code>元素来代表两个乘数; 当它们的值变化时, 会通过myWorker.postMessage([first.value,second.value])传递新值给worker, 数组意味着. 你可以往消息里塞入非常多的信息。<br>在worker里,我们可以通过编写一个事件回调函数来响应接受到的消息:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**worker.js**/</span></div><div class="line">onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Message received from main script'</span>);</div><div class="line">  <span class="keyword">var</span> workerResult = <span class="string">'Result: '</span> + (e.data[<span class="number">0</span>] * e.data[<span class="number">1</span>]);</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Posting message back to main script'</span>);</div><div class="line">  postMessage(workerResult);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>onmessage 处理程序允许我们在接受消息时运行一些代码，并在事件对象的data属性里访问传入的消息。这里我们简单的讲两个数字相乘，并再次使用postMessage()来回传结果回主线程。</p>
<p>回到主线程，我们再次使用onmessage 来响应来自于worker的消息：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**main.js**/</span></div><div class="line">myWorker.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  result.textContent = e.data;</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Message received from worker'</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>这里我们抓取事件data属性里的消息并设置到文档内容上展示出来。</p>
<hr>
<ul>
<li><p>注意: 传入到Worker 构造器里的参数必须遵循同源策略。</p>
<p>  浏览器制造商对于“同源”所包含的概念有争议；Gecko 10.0 (Firefox 10.0 / Thunderbird 10.0 / SeaMonkey 2.7) 以及之后的版本允许 data URIs，而Internet Explorer 10禁止Blob URIs作为用于worker的合法的脚本。</p>
</li>
<li><p>注意:  注意到，再主线程里onmessage 和postMessage()必须挂在Worker对象上 ，但是在worker内部不需要。这是因为在worker内部，该worker本身就有效的充当了全局作用域</p>
</li>
<li><p>注意: 当主线程和worker之间传递消息时，传递的是消息的副本，而不是共享该消息。</p>
</li>
</ul>
<hr>
<h4 id="关闭一个-worker"><a href="#关闭一个-worker" class="headerlink" title="关闭一个 worker"></a>关闭一个 worker</h4><ul>
<li><p>terminate()</p>
<p>  如果想在主线程里立刻关闭正在运行的worker，只要调用worker的terminate 方法就行：</p>
  <figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**main.js**/</span></div><div class="line">myWorker.terminate();</div></pre></td></tr></table></figure>
<p>  该worker线程会被立刻杀死而不会获得任何完成操作或者垃圾回收的机会。</p>
</li>
<li><p>close()</p>
<p>  在 worker 线程内部, workers 可以通过调用自己全局作用域下的close 方法来关闭自身:</p>
  <figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**worker.js**/</span></div><div class="line">self.close();</div></pre></td></tr></table></figure>
</li>
</ul>
<p>尽量使用close在worker内关闭自己，这样可以让线程被安全关闭且更适当地释放资源。具体算法参考下一节。</p>
<h4 id="生成子worker"><a href="#生成子worker" class="headerlink" title="生成子worker"></a>生成子worker</h4><p>如果需要，worker可以产生更多的worker。所谓的子worker必须是和父页面同源。而且，该子worker的URIs 的解析是相对于父worker的地址而不是其所属页面的。这简化了worker的依赖追踪。</p>
<h4 id="引入脚本和库"><a href="#引入脚本和库" class="headerlink" title="引入脚本和库"></a>引入脚本和库</h4><p>Worker现场可以访问全局函数，importScripts()，让worker引入脚本。它接受0个或者更多要导入的资源的URI作为参数；以下所有例子都是有效的：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">importScripts();                         <span class="comment">/* imports nothing */</span></div><div class="line">importScripts(<span class="string">'foo.js'</span>);                 <span class="comment">/* imports just "foo.js" */</span></div><div class="line">importScripts(<span class="string">'foo.js'</span>, <span class="string">'bar.js'</span>);       <span class="comment">/* imports two scripts */</span></div><div class="line">importScripts(<span class="string">'//example.com/hello.js'</span>); <span class="comment">/* You can import scripts from other origins */</span></div></pre></td></tr></table></figure>
<p>浏览器加载列出的所有脚本并且执行它们。之后所有脚本里的全局对象都能被worker所使用。如果脚本加载失败则会抛出一个<code>NETWORK_ERROR</code>错误，并且之后的代码将不会被执行。之前已经执行的代码（包括使用 window.setTimeout()延迟执行的代码）将继续执行。在importScripts() 方法之后的函数声明会被保留，因为它们始终比其他代码先解析。</p>
<hr>
<ul>
<li>注意: 脚本下载是无序的，但是执行顺序则根据传递到importScripts()里的顺序。这是异步完成的；在所有脚本都被下载和执行前，importScripts()不会返回。</li>
</ul>
<hr>
<h3 id="共享worker"><a href="#共享worker" class="headerlink" title="共享worker"></a>共享worker</h3><p>一个 <strong>共享worker</strong> 可以被多个脚本所访问 — 即使它们正在被多个不同的窗口访问着, 比如多个iframe乃至多个worker。 在这一节我们将会讨论 <a href="https://mdn.github.io/simple-shared-worker/" target="_blank" rel="external">Basic shared worker example</a>里的代码: 它和基础专属worker的例子很类似,除了它有两个函数可以被不同的脚本文件处理：两数相乘和平方一个数字。两个脚本使用同一个worker 来完成所需的实际计算。</p>
<p>这里我们将重点分析专属和共享worker之间的不同。注意到在这个例子里我们有两个HTML页面，每个页面都有JavaScript 访问同一个worker文件。</p>
<hr>
<ul>
<li>注意: 如果共享worker可以被来自不同的浏览上下文所访问，那这些浏览上下文必须时完全同源的（同协议、域名和端口）。</li>
<li>注意: 在火狐里,共享worker不能在一个隐私模式下的窗口和一个非隐私模式下的窗口之间共享 (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1177621" target="_blank" rel="external">bug 1177621</a>)。</li>
</ul>
<hr>
<h4 id="生成一个-shared-worker"><a href="#生成一个-shared-worker" class="headerlink" title="生成一个 shared worker"></a>生成一个 shared worker</h4><p>与生成一个新dedicated worker类似, 但是不同的是构造器的名称是<code>SharedWorker</code> — 每个都得使用与下面类似的方式启动worker:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">var</span> myWorker = <span class="keyword">new</span> SharedWorker(<span class="string">"worker.js"</span>);</div></pre></td></tr></table></figure></p>
<p>一个最大的不同点在于使用共享worker你必须通过一个<code>port对象</code>来通信——明确指定一个端口，这样脚本们可以通过它来与worker通信。(在专属worker偷偷完成了这一点)。</p>
<p>端口链接需要通过隐式地使用<code>onmessage</code>事件处理程序或者显式地在传递任何消息前使用 start()方法。只有当消息事件通过是addEventListener()接通的情况下需要调用start()。</p>
<p>当使用<code>start()</code>方法来开启端口连接时, 如果需要双向通信，则该方法既要在主线程中调用，也需要在woker线程中调用。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// called in parent thread</span></div><div class="line">myWorker.port.start();</div><div class="line"></div><div class="line"><span class="comment">// called in worker thread, assuming the port variable references a port</span></div><div class="line">port.start();</div></pre></td></tr></table></figure></p>
<h4 id="往shared-worker里发送消息以及从shared-worker发出消息"><a href="#往shared-worker里发送消息以及从shared-worker发出消息" class="headerlink" title="往shared worker里发送消息以及从shared worker发出消息"></a>往shared worker里发送消息以及从shared worker发出消息</h4><p>现在消息可以像之前一样传递到worker里了,但是postMessage() 方法必须通过port对象来调用 :</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">squareNumber.onchange = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</div><div class="line">  myWorker.port.postMessage([squareNumber.value,squareNumber.value]);</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Message posted to worker'</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>现在, 将目光转向我们的worker。这里也发生了些复杂的事情:<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="comment">/**worker.js**/</span></div><div class="line">onconnect = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">    <span class="keyword">var</span> port = e.ports[<span class="number">0</span>];</div><div class="line"></div><div class="line">    port.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">        <span class="keyword">var</span> workerResult = <span class="string">'Result: '</span> + (e.data[<span class="number">0</span>] * e.data[<span class="number">1</span>]);</div><div class="line">        port.postMessage(workerResult);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>首先, 我们使用一个<code>onconnect事件处理程序</code>来在发生一个端口连接的时候去激活代码(比如，当父线程里设置了一个<code>onmessage事件处理程序</code>，或者在父线程里<code>start()</code>方法被显式调用)。</p>
<p>我们使用该事件的<code>ports属性</code>来获取端口并且将它存到一个变量里。</p>
<p>之后，我们增加一个消息处理程序在该端口上来做计算并返回给主线程。设置该消息处理程序在worker线程里同样隐式打开了到父线程的端口连接，所以无需调用 <code>port.start()</code>。</p>
<p>最后, 回到main 脚本, 我们处理worker返回的消息:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">myWorker.port.onmessage = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>&#123;</div><div class="line">  result2.textContent = e.data;</div><div class="line">  <span class="built_in">console</span>.log(<span class="string">'Message received from worker'</span>);</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>当一个消息通过端口回到主线程时，我们检查结果类型，然后插入到文档里显示出来。</p>
<h2 id="web-worker的生命周期和处理模型"><a href="#web-worker的生命周期和处理模型" class="headerlink" title="web worker的生命周期和处理模型"></a>web worker的生命周期和处理模型</h2><h3 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h3><p>web worker之间的通信必须依赖于浏览器的上下文环境，并且通过它们的 MessagePort 对象实例传递消息。每个web worker的全局作用域都拥有这些线程的端口列表，这些列表包括了所有线程使用到的 MessagePort 对象。在专用线程的情况下，这个列表还会包含隐式的 MessagePort 对象。</p>
<p>每个web worker的全局作用域对象 WorkerGlobalScope 还会有一个web worker的线程列表，在初始化时这个列表为空。当web worker被创建的时候或者拥有父web worker的时候，它们就会被填充进来。</p>
<p>最后，每个web worker的全局作用域对象 WorkerGlobalScope 还拥有这个线程的文档对象列表，在初始化时这个列表为空。当web worker被创建的时候，文档对象就会被填充进来。无论何时当一个文档对象被丢弃的时候，它就会从这个文档对象列表里面被删除。<br>在web worker的生命周期中，定义了下面四种不同类型的线程名称，用以标识它们在线程的整个生命周期中的不同状态：</p>
<ul>
<li>当一个web worker的<code>文档列表</code>不为空的时候，这个web worker会被称之为<code>许可线程</code>。</li>
<li>当一个web worker的<code>文档列表</code>中的任何一个对象都是处于完全活动状态的时候，这个web worker会被称之为<code>需要激活线程</code>。</li>
<li>当一个web worker是许可线程并且拥有计数器或者拥有数据库事务或者拥有网络连接或者它的web worker列表不为空的时候，这个web worker会被称之为<code>受保护的线程</code>。</li>
<li>当一个web worker是一个非需要激活线程同时又是一个许可线程的时候，这个web worker会被称之为<code>可挂起线程</code>。</li>
</ul>
<p>由于 W3C 的 Web Worker 规范目前还是处于完善阶段，没有形成最终的规范。</p>
<h3 id="处理模型"><a href="#处理模型" class="headerlink" title="处理模型"></a>处理模型</h3><p><img src="http://p1.meituan.net/kuailv/1aae5f5a2fd5220d14bf377ffd82937840137.png" alt="" title="web worker执行模型"></p>
<p>当web worker被一个具有 URL 参数的构造函数创建的时候，它需要有一系列的处理流程来处理和记录它本身的数据和状态。<a href="https://html.spec.whatwg.org/multipage/workers.html#worker-processing-model" target="_blank" rel="external">W3C的web worker的处理模型</a>如下：</p>
<blockquote>
<p>当一个用户代理将要通过：Worker 或者 SharedWorker等worker对象、<a href="https://url.spec.whatwg.org/#concept-url" target="_blank" rel="external">URL</a> url, <a href="https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object" target="_blank" rel="external">环境设置对象</a><code>外部设置</code>, <a href="https://html.spec.whatwg.org/multipage/comms.html#messageport" target="_blank" rel="external">MessagePort</a> <code>外部端口</code>, 一个<a href="https://html.spec.whatwg.org/multipage/workers.html#workeroptions" target="_blank" rel="external">WorkerOptions</a> 字典 <code>options参数</code>, 和一个可选字符串<code>name</code> 来为一个脚本 <strong>运行一个线程</strong> 时，它必须执行以下步骤：（当worker是一个共享worker时，name始终会被提供。）</p>
<ol>
<li>创建一个独立的并行处理环境，并且在这个环境里面异步的运行下面的步骤。<hr>
对于那些用于计时的API来说，这是官方规定的worker创建时刻。<hr>
</li>
<li>让<code>is shared</code>设置为true如果worker是一个 <code>SharedWorker</code> 对象, 反之为 false。</li>
<li>让<code>docs</code>为<a href="https://html.spec.whatwg.org/multipage/workers.html#list-of-relevant-document-objects-to-add" target="_blank" rel="external">相关的Document对象组成的列表</a>并用来添加给定的外部设置。</li>
<li>让父 worker 的<code>parent worker global scope</code>为 null.</li>
<li>如果外部设置的<a href="https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-global" target="_blank" rel="external">全局对象</a>(即<a href="https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object" target="_blank" rel="external">环境设置对象</a>)是一个 WorkerGlobalScope 对象（比如我们在创建嵌套worker时），设置<code>parent worker global scope</code>到外部设置的<a href="https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-global" target="_blank" rel="external">全局对象上</a>。</li>
<li><p>通过以下自定义项来调用JavaScript <a href="https://tc39.github.io/ecma262/#sec-initializehostdefinedrealm" target="_blank" rel="external">InitializeHostDefinedRealm()</a>抽象操作 </p>
<ul>
<li><p>对于全局对象，如果<code>is shared</code>是true,创建一个新 SharedWorkerGlobalScope对象。否则，创建一个新DedicatedWorkerGlobalScope对象。并使其成为该worker的全局作用域对象。</p>
</li>
<li><p>让区域执行上下文（<a href="https://tc39.github.io/ecma262/#realm" target="_blank" rel="external">realm execution context</a>,概念上类似于一个ECMAScript 全局环境）成为所创建的<a href="https://tc39.github.io/ecma262/#sec-execution-contexts" target="_blank" rel="external">JavaScript执行上下文</a>。</p>
</li>
</ul>
</li>
<li>利用上述的区域执行上下文和外部设置来<a href="https://html.spec.whatwg.org/multipage/workers.html#set-up-a-worker-environment-settings-object" target="_blank" rel="external">初始化一个worker环境设置对象</a>，并且将返回的设置对象设置为<code>内部设置</code>。</li>
<li>如果<code>is shared</code>为true, 则:<ol>
<li>设置worker全局作用域的<a href="https://html.spec.whatwg.org/multipage/workers.html#concept-sharedworkerglobalscope-constructor-origin" target="_blank" rel="external">constructor origin属性</a>值为外部设置的源。</li>
<li>设置worker全局作用域的<a href="https://html.spec.whatwg.org/multipage/workers.html#concept-sharedworkerglobalscope-constructor-url" target="_blank" rel="external">constructor url属性</a>的值为外部设置的url。</li>
<li>设置worker全局作用域的<a href="https://html.spec.whatwg.org/multipage/workers.html#concept-sharedworkerglobalscope-constructor-url" target="_blank" rel="external">name属性</a>的值为外部设置的name。</li>
</ol>
</li>
<li>把<a href="https://fetch.spec.whatwg.org/#concept-request-destination" target="_blank" rel="external">destination</a>值设置为”sharedworker”如果<code>is shared</code>为true，否则为”worker”。</li>
<li><p>通过options参数的type成员的值来获取脚本:</p>
<ul>
<li><p>“classic”<br>  利用指定的url来获取经典的worker脚本、外部设置、 destination和内部设置。</p>
</li>
<li><p>“module”<br>利用指定的url来获取脚本图、外部设置、 destination、options参数的credentials成员值和<code>内部设置</code>。</p>
</li>
</ul>
<p>两种情况下，对请求<a href="https://html.spec.whatwg.org/multipage/webappapis.html#fetching-scripts-perform-fetch" target="_blank" rel="external">执行fetch操作</a>，在<a href="https://html.spec.whatwg.org/multipage/webappapis.html#fetching-scripts-is-top-level" target="_blank" rel="external">is top-level flag</a>被设置后将以下步骤：</p>
<ol>
<li>设置请求的 <a href="https://fetch.spec.whatwg.org/#concept-request-reserved-client" target="_blank" rel="external">reserved client</a>（即环境） 为<code>内部设置</code>。</li>
<li><a href="https://fetch.spec.whatwg.org/#concept-fetch" target="_blank" rel="external">Fetch</a> 请求,并且为了执行残留的步骤而异步等待，残留的步骤作为fetch的<a href="https://fetch.spec.whatwg.org/#concept-response-url" target="_blank" rel="external">process response</a>对应<a href="https://fetch.spec.whatwg.org/#concept-response" target="_blank" rel="external">response</a>（fetch的结果）的一部分。</li>
<li>设置<code>worker全局作用域</code>的 <a href="https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-url" target="_blank" rel="external">url</a> 为响应的 <a href="https://fetch.spec.whatwg.org/#concept-response-url" target="_blank" rel="external">url</a>。</li>
<li>设置 <code>worker 全局作用域</code>的 <a href="https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-https-state" target="_blank" rel="external">HTTPS 状态</a>为响应的 <a href="https://fetch.spec.whatwg.org/#concept-response-https-state" target="_blank" rel="external">HTTPS 状态</a>。</li>
<li>设置 <code>worker 全局作用域</code>的 referrer policy 为处理响应报文头部的<code>Referrer-Policy</code>字段所的到结果。</li>
<li>在<code>worker的全局作用域</code>和响应上执行<a href="https://w3c.github.io/webappsec-csp/#initialize-global-object-csp" target="_blank" rel="external">初始化全局对象的CSP列表</a>算法。[<a href="https://html.spec.whatwg.org/multipage/references.html#refsCSP" target="_blank" rel="external">CSP(内容安全策略)</a>]</li>
<li>异步地利用响应来完成<a href="https://html.spec.whatwg.org/multipage/webappapis.html#fetching-scripts-perform-fetch" target="_blank" rel="external">执行fetch操作</a>步骤。</li>
</ol>
<p>如果异步执行该算法得到的结果为null，则排队一个任务来发射一个error事件，并且放弃这些步骤。否则，在该算法异步执行完后继续执行之后的步骤。</p>
</li>
<li>将worker和worker全局作用域关联起来。</li>
<li><a href="https://html.spec.whatwg.org/multipage/comms.html#create-a-new-messageport-object" target="_blank" rel="external">创建一个新MessagePort对象</a>，其<a href="https://html.spec.whatwg.org/multipage/comms.html#concept-port-owner" target="_blank" rel="external">所有者</a>是<code>内部设置</code>。让<code>内部端口</code>为新建的对象。</li>
<li>将 <code>内部端口</code> 和 worker 全局作用域关联起来.</li>
<li>将<code>外部端口</code>和<code>内部端口</code>牵连起来。</li>
<li>将<code>文档</code>的<a href="https://html.spec.whatwg.org/multipage/dom.html#document" target="_blank" rel="external">Document</a>对象<a href="https://html.spec.whatwg.org/multipage/workers.html#add-a-document-to-the-worker&#39;s-documents" target="_blank" rel="external">添加到<code>worker的全局作用域</code>下的<code>文档列表</code>里</a>。</li>
<li>如果<code>parent worker global scope</code>不是null，则添加该worker的<code>worker的全局作用域</code>到<code>parent worker global scope</code>的<a href="https://html.spec.whatwg.org/multipage/workers.html#the-worker&#39;s-workers" target="_blank" rel="external">workers列表</a>里。</li>
<li>设置 <code>worker的全局作用域</code>的 <a href="https://html.spec.whatwg.org/multipage/workers.html#concept-workerglobalscope-type" target="_blank" rel="external">type</a> 为<code>options</code>的type成员的值。</li>
<li>创建一个新<a href="https://html.spec.whatwg.org/multipage/workers.html#workerlocation" target="_blank" rel="external">WorkerLocation</a>对象并且将它和<code>worker的全局作用域</code>关联。</li>
<li><strong>关闭孤儿worker</strong>: 开始监控线程，在晚于其不再是一个<code>受保护线程</code>且早于它不再是一个<code>许可线程</code>时，设置<code>worker的全局作用域</code>的<a href="https://html.spec.whatwg.org/multipage/workers.html#dom-workerglobalscope-closing" target="_blank" rel="external">closing</a> flag为true。</li>
<li><strong>挂起线程</strong>: 开始监控线程，在worker全局作用域的<a href="https://html.spec.whatwg.org/multipage/workers.html#dom-workerglobalscope-closing" target="_blank" rel="external">closing</a> flag为false并且worker是一个<a href="https://html.spec.whatwg.org/multipage/workers.html#suspendable-worker" target="_blank" rel="external">可挂起线程</a>时，用户代理挂起该线程的脚本的执行直到<a href="https://html.spec.whatwg.org/multipage/workers.html#dom-workerglobalscope-closing" target="_blank" rel="external">closing</a> flag为true或者worker不再是一个<a href="https://html.spec.whatwg.org/multipage/workers.html#suspendable-worker" target="_blank" rel="external">可挂起线程</a>。</li>
<li>设置 <code>内部设置</code>的<a href="https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-execution-ready-flag" target="_blank" rel="external">execution ready flag</a>。</li>
<li><p>如果脚本是一个<a href="https://html.spec.whatwg.org/multipage/webappapis.html#classic-script" target="_blank" rel="external">经典脚本</a>，则<a href="https://html.spec.whatwg.org/multipage/webappapis.html#run-a-classic-script" target="_blank" rel="external">执行该经典脚本</a>操作。否则，该脚本就是一个<a href="https://html.spec.whatwg.org/multipage/webappapis.html#module-script" target="_blank" rel="external">模块脚本</a>并<a href="https://html.spec.whatwg.org/multipage/webappapis.html#run-a-module-script" target="_blank" rel="external">执行模块脚本</a>操作</p>
<p>除了返回一个值或者由于异常而失败之外，这也可能由于下面定义的”<a href="https://html.spec.whatwg.org/multipage/workers.html#kill-a-worker" target="_blank" rel="external">杀死一个worker线程</a>“或者”<a href="https://html.spec.whatwg.org/multipage/workers.html#terminate-a-worker" target="_blank" rel="external">结束一个worker线程</a>“算法而被提前放弃。</p>
</li>
<li><p>启动<code>外部端口</code>的<a href="https://html.spec.whatwg.org/multipage/comms.html#port-message-queue" target="_blank" rel="external">端口消息队列</a>。</p>
</li>
<li><p>如果<code>is shared</code>为false，则开启worker的隐藏端口的<a href="https://html.spec.whatwg.org/multipage/comms.html#port-message-queue" target="_blank" rel="external">端口消息队列</a>。</p>
</li>
<li>如果<code>is shared</code>为true, 则排队一个任务, 在<code>worker的全局作用域</code>中使用<a href="https://html.spec.whatwg.org/multipage/webappapis.html#dom-manipulation-task-source" target="_blank" rel="external">the DOM manipulation task source</a>来启动一个名为<a href="https://html.spec.whatwg.org/multipage/indices.html#event-workerglobalscope-connect" target="_blank" rel="external">connect</a>的事件，使用<a href="https://html.spec.whatwg.org/multipage/comms.html#messageevent" target="_blank" rel="external">MessageEvent</a>，并将其<a href="https://html.spec.whatwg.org/multipage/comms.html#dom-messageevent-data" target="_blank" rel="external">data</a>特性初始化为空字符串,<a href="">ports</a>特性初始化为一个包含着<code>内部端口</code>的新<a href="https://heycam.github.io/webidl/#dfn-frozen-array-type" target="_blank" rel="external">frozen array</a>，并且<a href="https://html.spec.whatwg.org/multipage/comms.html#dom-messageevent-source" target="_blank" rel="external">source</a>特性初始化为<code>内部端口</code></li>
<li>启动<a href="https://w3c.github.io/ServiceWorker/#serviceworkercontainer" target="_blank" rel="external">ServiceWorkerContainer</a>对象的<a href="https://w3c.github.io/ServiceWorker/#dfn-client-message-queue" target="_blank" rel="external">客户端消息队列</a>，<a href="https://w3c.github.io/ServiceWorker/#serviceworkercontainer" target="_blank" rel="external">ServiceWorkerContainer</a>对象相关的<a href="https://w3c.github.io/ServiceWorker/#serviceworkercontainer-service-worker-client" target="_blank" rel="external">service worker client</a>是worker全局作用域的<a href="https://html.spec.whatwg.org/multipage/webappapis.html#relevant-settings-object" target="_blank" rel="external">相关设置对象</a>。</li>
<li><strong>Event loop</strong>: 执行由内部配置指定的<a href="https://html.spec.whatwg.org/multipage/webappapis.html#responsible-event-loop" target="_blank" rel="external">responsible event loop</a>直到其被摧毁。<hr>
responsible event loop:不会被使用该event loop的环境立刻回收的event loop。<hr>
由event loop运行的事件的处理或者<a href="https://html.spec.whatwg.org/multipage/webappapis.html#concept-task" target="_blank" rel="external">任务</a>的回调函数的执行可能由于下面定义的”<a href="https://html.spec.whatwg.org/multipage/workers.html#kill-a-worker" target="_blank" rel="external">杀死一个worker线程</a>“或者”<a href="https://html.spec.whatwg.org/multipage/workers.html#terminate-a-worker" target="_blank" rel="external">结束一个worker线程</a>“算法而被提前抛弃。<br>该worker处理模型将会循环这些步骤直到event loop被摧毁，这发生在<a href="https://html.spec.whatwg.org/multipage/workers.html#dom-workerglobalscope-closing" target="_blank" rel="external">closing</a> flag被设置为true之后,正如<a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loop" target="_blank" rel="external">event loop</a>处理模型里定义的那样。</li>
<li>清空该worker全局作用域下的活动中的计时器的列表。</li>
<li>释放worker的端口列表中的所有端口</li>
<li>清空worker的文档列表。</li>
</ol>
<p>当一个用户代理将要 <strong>杀死一个worker线程</strong>，它必须并行于worker的主循环来执行以下步骤:</p>
<ol>
<li>设置该 worker的 WorkerGlobalScope 对象的closing flag 为true.</li>
<li>如果 WorkerGlobalScope 对象的event loop里的任务队列里还有任务在排队，则直接抛弃它们而不执行。</li>
<li>等待一段由用户代理定义的时间。</li>
<li><p>终止worker中正在执行的脚本</p>
<p>用户代理可以在任意时刻请求“杀死一个worker”处理模型，比如用户请求，cpu配额管理，或者当一个worker不再是一个<code>需要激活线程</code>而该worker在其<code>closing flag</code>被设置为<code>true</code>后仍然继续执行时。</p>
</li>
</ol>
<p>当用户代理要 <strong>结束一个worker线程</strong>，它必须并行于worker的主循环来执行以下步骤:</p>
<ol>
<li>设置该 worker的 WorkerGlobalScope 对象的closing flag 为true.</li>
<li>如果 WorkerGlobalScope 对象的event loop里的任务队列里还有任务在排队，则直接抛弃它们而不执行。</li>
<li>等待一段由用户代理定义的时间。</li>
<li>如果该worker的 WorkerGlobalScope对象是一个DedicatedWorkerGlobalScope对象（比如该worker是一个dedicated worker），则清空该worker的隐藏端口所对应的端口的端口消息队列。</li>
</ol>
</blockquote>
<hr>
<p>注意：由于 whatwg 中web worker的规范依然在更新，您读到这篇文章的时候可能看到已不是最新的处理模型，建议参考 W3C 中的最新规范。</p>
<hr>
<h2 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h2><p>当worker内出现了一个运行时错误，它的onerror 事件处理程序会被调用。该事件处理程序会收到一个名为error的事件，它实现了ErrorEvent 接口。<br>该事件可以被取消但是不会冒泡；为了阻止默认行为的发生，该worker可以调用错误事件的<code>preventDefault()</code> 方法。<br>错误事件有以下三个域:</p>
<ul>
<li>message<br>  一个人工可读的错误消息。</li>
<li>filename<br>  发生错误的脚本文件的名字。</li>
<li>lineno<br>  发生错误的代码在脚本文件中的行数。</li>
</ul>
<h3 id="深入规范"><a href="#深入规范" class="headerlink" title="深入规范"></a>深入规范</h3><p>当worker的脚本发生运行时错误时,如果错误没有在处理上一个错误期间发生，则用户代理必需为该脚本所发生的错误, 带上发生错误的代码位置信息（行数和列数）, 使用 WorkerGlobalScope 对象作为目标。</p>
<p>对于共享worker， 如果错误在之后仍未被处理， 则错误可能会被报告给用户。</p>
<p>对于专属worker，如果该错误在之后仍未被处理，则用户代理必需排队一个任务来以发射出一个使用ErrorEvent接口实现的受信任的名为error的事件，并带上其消息、文件名、行数、列数和被恰当初始化过的特性，并且错误特性被初始化为null，且该事件不冒泡并且可以在该worker对应的Worker对象上被取消。如果事件没被取消，则用户代理必需表现得像一个未处理的运行时错误发生在Worker对象的全局作用域下那样，逐级往上地重复整个运行时脚本错误报告处理流程。</p>
<p>如果该worker正在连接着的隐藏端口已经被释放了（比如，如果父worker已经被结束），则该用户代理必需表现的像该the Workerobject没有错误事件处理函数并且该worker的onerror特性是null那样。如果端口未被释放，则执行前述的流程。</p>
<hr>
<p>因此，错误报告会从专属worker链一直冒泡到主文档，即使整条链上的一些worker已经被结束并且被垃圾回收。而错误事件则不会冒泡。</p>
<hr>
<h2 id="数据传输"><a href="#数据传输" class="headerlink" title="数据传输"></a>数据传输</h2><p>主线程与子线程数据通信方式有多种，通信内容，可以是文本，也可以是对象。主线程与子线程之间也可以交换二进制数据，比如File、Blob、ArrayBuffer等对象，也可以在线程之间发送。比如，主线程向子线程发送一个50MB文件，默认情况下浏览器会生成一个原文件的拷贝。</p>
<h3 id="通过-cloneable-objects传输数据"><a href="#通过-cloneable-objects传输数据" class="headerlink" title="通过 cloneable objects传输数据"></a>通过 <a href="http://w3c.github.io/html/infrastructure.html#safe-passing-of-structured-data" target="_blank" rel="external">cloneable objects</a>传输数据</h3><p>这种通信是深拷贝关系， 即是传值而不是地址，子线程对通信内容的修改，不会影响到主线程。<br>在传输过程中使用的克隆算法叫<a href="http://w3c.github.io/html/infrastructure.html#structuredclone" target="_blank" rel="external">structured clone algorithm</a>,这种算法允许传输大部分类型，包括：</p>
<ul>
<li>正则对象。</li>
<li>Blob、 File 和 FileList 对象。</li>
<li>ImageData对象。</li>
<li>unJSON-safe的对象。</li>
</ul>
<hr>
<p>早期浏览器内部的运行机制是，先将通信内容串行化，然后把串行化后的字符串发给子线程，后者再将它还原。</p>
<hr>
<p>不能传输的数据有：</p>
<ul>
<li>Error和Function对象，尝试克隆会抛出<code>DATA_CLONE_ERR</code>错误。</li>
<li>DOM节点，尝试克隆会抛出<code>DATA_CLONE_ERR</code>错误。</li>
<li>对象的特定参数不会被保留：<ul>
<li>RegExp对象的lastIndex 域不会被保留。</li>
<li>属性描述符、数据描述符不会被复制。</li>
<li>原型链不会被遍历和复制。</li>
</ul>
</li>
</ul>
<h3 id="通过-transferable-objects传输数据"><a href="#通过-transferable-objects传输数据" class="headerlink" title="通过 transferable objects传输数据"></a>通过 <a href="http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#transferable-objects" target="_blank" rel="external">transferable objects</a>传输数据</h3><p>但是，用拷贝方式发送二进制数据，会造成性能问题。为了解决这个问题，规范定义了<code>Transferable objects</code>，通过控制权移交的方式，将对象从主线程直接转移给子线程，转移后主线程无法再使用这些数据，这是为了防止出现多个线程同时修改数据的问题。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Create a 32MB "file" and fill it.</span></div><div class="line"><span class="keyword">var</span> uInt8Array = <span class="keyword">new</span> <span class="built_in">Uint8Array</span>(<span class="number">1024</span>*<span class="number">1024</span>*<span class="number">32</span>); <span class="comment">// 32MB</span></div><div class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; uInt8Array .length; ++i) &#123;</div><div class="line">    uInt8Array[i] = i;</div><div class="line">&#125;</div><div class="line">worker.postMessage(uInt8Array.buffer, [uInt8Array.buffer]);</div></pre></td></tr></table></figure>
<h2 id="关于线程安全"><a href="#关于线程安全" class="headerlink" title="关于线程安全"></a>关于线程安全</h2><p>Worker接口生成真正的系统级别的线程，有心的程序员会想到如果不太小心，那么并行会对你的代码产生有趣的影响。</p>
<p>然而，对于 web worker 来说，与其他线程的通信点会被很细心地控制，这就意味着你很难引起并行问题。你没有办法去访问非线程安全的组件或者是 DOM，此外你还需要通过序列化对象作为数据来在线程间传递。所以你要是不费点劲儿，还真搞不出错误来。</p>
<h2 id="内容安全策略"><a href="#内容安全策略" class="headerlink" title="内容安全策略"></a>内容安全策略</h2><p>Worker们被视为有它们自己的执行上下文，不同于创建它们的文档。出于这个原因，一般而言，worker不受创建它们的文档（或者父worker）的内容安全策略所约束。所以，举个例子，假设一个服务端返回的文档有如下的头部：<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Content-Security-Policy: script-src &apos;self&apos;</div></pre></td></tr></table></figure></p>
<p>这将会阻止它所包含的任何脚本执行eval()。但是，如果脚本构造了一个worker，在worker上下文中运行的代码将会被允许使用eval()。<br>为了给worker指定内容安全策略，需要为请求的worker脚本的响应报文设置一个  Content-Security-Policy 响应头部。<br>例外，在worker脚本的源是全局唯一标识（比如，当它的URL有一个 Data URI scheme或者blob）的时候，这周情况下，该worker就会继承创建它的文档或者worker的内容安全策略。</p>
<h2 id="浏览器支持"><a href="#浏览器支持" class="headerlink" title="浏览器支持"></a><a href="http://caniuse.com/#search=web%20worker" target="_blank" rel="external">浏览器支持</a></h2><p><img src="http://p0.meituan.net/kuailv/2eeab3203902eee3bf8565f7fbedaace198201.png" alt="" title="浏览器支持"></p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="https://html.spec.whatwg.org/multipage/workers.html" target="_blank" rel="external">HTML living standard — Last Updated 13 April 2017</a></li>
<li><a href="http://w3c.github.io/html/infrastructure.html#transferable-objects" target="_blank" rel="external">HTML 5.2 Editor’s Draft, 9 April 2017</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API" target="_blank" rel="external">MDN - Web Workers API</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/AbstractWorker" target="_blank" rel="external">MDN - AbstractWorker</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker" target="_blank" rel="external">MDN - Worker</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker" target="_blank" rel="external">MDN - SharedWorker</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope" target="_blank" rel="external">MDN - WorkerGlobalScope</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope" target="_blank" rel="external">MDN - DedicatedWorkerGlobalScope</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/SharedWorkerGlobalScope" target="_blank" rel="external">MDN - SharedWorkerGlobalScope</a></li>
</ul>
</span>
      
    </div>

    <footer class="post-footer">

      
        <div class="post-nav">
          <div class="post-nav-prev post-nav-item">
            
              <a href="/2017/05/04/y8n/understand-virtual-dom/" rel="prev">理解Virtual DOM</a>
            
          </div>

          <div class="post-nav-next post-nav-item">
            
              <a href="/2017/05/03/zhouxiong/Thrift/" rel="next">远程调用框架Thrift分享</a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
        <!-- JiaThis Button BEGIN -->
<div class="jiathis_style">
  <a class="jiathis_button_tsina"></a>
  <a class="jiathis_button_tqq"></a>
  <a class="jiathis_button_weixin"></a>
  <a class="jiathis_button_cqq"></a>
  <a class="jiathis_button_douban"></a>
  <a class="jiathis_button_renren"></a>
  <a class="jiathis_button_qzone"></a>
  <a class="jiathis_button_kaixin001"></a>
  <a class="jiathis_button_copy"></a>
  <a href="http://www.jiathis.com/share" class="jiathis jiathis_txt jiathis_separator jtico jtico_jiathis" target="_blank"></a>
  <a class="jiathis_counter_style"></a>
</div>
<script type="text/javascript" >
  var jiathis_config={
    hideMore:false
  }
</script>
<script type="text/javascript" src="http://v3.jiathis.com/code/jia.js" charset="utf-8"></script>
<!-- JiaThis Button END -->

      
    </div>
  </div>

 </div>

        

        
          <div class="comments" id="comments">
            <div id="SOHUCS" sid="" ></div>
          </div>
        
      </div>

      
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <a href="https://github.com/xgfe" target="_blank"><img class="site-author-image" src="http://p0.meituan.net/xgfe/082a9624ba5ae8602150a2d43968463e49348.png" alt="xgfe" itemprop="image"/></a>
          <p class="site-author-name" itemprop="name">xgfe</p>
        </div>
        <p class="site-description motion-element" itemprop="description">xgfe's blog. 鲜果前端的技术博客，鲜果前端研发部官方博客。前端基础技术研究：html, html5, javascript, css, css3；前端框架研究：angularJs, react, react native.</p>
        <nav class="site-state motion-element">
          <div class="site-state-item site-state-posts">
            <a href="/archives">
              <span class="site-state-item-count">89</span>
              <span class="site-state-item-name">日志</span>
            </a>
          </div>

          <div class="site-state-item site-state-categories">
            
              <span class="site-state-item-count">37</span>
              <span class="site-state-item-name">作者</span>
              
          </div>

          <div class="site-state-item site-state-tags">
            <a href="/tags">
              <span class="site-state-item-count">131</span>
              <span class="site-state-item-name">标签</span>
              </a>
          </div>

        </nav>

        
          <div class="feed-link motion-element">
            <a href="/atom.xml" target="_blank" rel="alternate">
              <i class="menu-item-icon icon-next-feed"></i>
              RSS
            </a>
          </div>
        

        <div class="links-of-author motion-element">
          
            
              <span class="links-of-author-item">
                <a href="https://github.com/xgfe" target="_blank">GitHub</a>
              </span>
            
          
        </div>

        
        

        <div class="links-of-author motion-element">
          
        </div>

      </section>

      
        <section class="post-toc-wrap sidebar-panel-active">
          <div class="post-toc-indicator-top post-toc-indicator"></div>
          <div class="post-toc">
            
            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#Web-Workers-概念"><span class="nav-number">1.</span> <span class="nav-text">Web Workers 概念</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#使用web-worker"><span class="nav-number">2.</span> <span class="nav-text">使用web worker</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Worker-特征检测"><span class="nav-number">2.1.</span> <span class="nav-text">Worker 特征检测</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#专属worker"><span class="nav-number">2.2.</span> <span class="nav-text">专属worker</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#生成一个专属worker"><span class="nav-number">2.2.1.</span> <span class="nav-text">生成一个专属worker</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#往dedicated-worker里发送消息以及从dedicated-worker发出消息"><span class="nav-number">2.2.2.</span> <span class="nav-text">往dedicated worker里发送消息以及从dedicated worker发出消息</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#关闭一个-worker"><span class="nav-number">2.2.3.</span> <span class="nav-text">关闭一个 worker</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#生成子worker"><span class="nav-number">2.2.4.</span> <span class="nav-text">生成子worker</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#引入脚本和库"><span class="nav-number">2.2.5.</span> <span class="nav-text">引入脚本和库</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#共享worker"><span class="nav-number">2.3.</span> <span class="nav-text">共享worker</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#生成一个-shared-worker"><span class="nav-number">2.3.1.</span> <span class="nav-text">生成一个 shared worker</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#往shared-worker里发送消息以及从shared-worker发出消息"><span class="nav-number">2.3.2.</span> <span class="nav-text">往shared worker里发送消息以及从shared worker发出消息</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#web-worker的生命周期和处理模型"><span class="nav-number">3.</span> <span class="nav-text">web worker的生命周期和处理模型</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#生命周期"><span class="nav-number">3.1.</span> <span class="nav-text">生命周期</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#处理模型"><span class="nav-number">3.2.</span> <span class="nav-text">处理模型</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#错误处理"><span class="nav-number">4.</span> <span class="nav-text">错误处理</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#深入规范"><span class="nav-number">4.1.</span> <span class="nav-text">深入规范</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#数据传输"><span class="nav-number">5.</span> <span class="nav-text">数据传输</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#通过-cloneable-objects传输数据"><span class="nav-number">5.1.</span> <span class="nav-text">通过 cloneable objects传输数据</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#通过-transferable-objects传输数据"><span class="nav-number">5.2.</span> <span class="nav-text">通过 transferable objects传输数据</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#关于线程安全"><span class="nav-number">6.</span> <span class="nav-text">关于线程安全</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#内容安全策略"><span class="nav-number">7.</span> <span class="nav-text">内容安全策略</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#浏览器支持"><span class="nav-number">8.</span> <span class="nav-text">浏览器支持</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#参考资料"><span class="nav-number">9.</span> <span class="nav-text">参考资料</span></a></li></ol></div>
            
          </div>
          <div class="post-toc-indicator-bottom post-toc-indicator"></div>
        </section>
      

    </div>
  </aside>


    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner"> <div class="copyright" >
  
  &copy; &nbsp; 
  <span itemprop="copyrightYear">2018</span>
  <span class="with-love">
    <i class="icon-next-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">xgfe</span>
</div>

<div class="powered-by">
  由 <a class="theme-link" target="_blank" href="http://hexo.io">Hexo</a> 强力驱动
</div>

<div class="theme-info">
  主题 -
  <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">
    NexT.Mist
  </a>
</div>


 </div>
    </footer>

    <div class="back-to-top"></div>
  </div>

  <script type="text/javascript" src="/vendors/jquery/index.js?v=2.1.3"></script>

  
  
  
    <script type="text/javascript"> 
(function(){ 
var appid = 'cysWiXvkm'; 
var conf = 'prod_fc970dbe85103c7a79b2c4f3dc7fb190'; 
var width = window.innerWidth || document.documentElement.clientWidth; 
if (width < 960) { 
window.document.write('<script id="changyan_mobile_js" charset="utf-8" type="text/javascript" src="http://changyan.sohu.com/upload/mobile/wap-js/changyan_mobile.js?client_id=' + appid + '&conf=' + conf + '"><\/script>'); } else { var loadJs=function(d,a){var c=document.getElementsByTagName("head")[0]||document.head||document.documentElement;var b=document.createElement("script");b.setAttribute("type","text/javascript");b.setAttribute("charset","UTF-8");b.setAttribute("src",d);if(typeof a==="function"){if(window.attachEvent){b.onreadystatechange=function(){var e=b.readyState;if(e==="loaded"||e==="complete"){b.onreadystatechange=null;a()}}}else{b.onload=a}}c.appendChild(b)};loadJs("http://changyan.sohu.com/upload/changyan.js",function(){window.changyan.api.config({appid:appid,conf:conf})}); } })(); </script>
    

  


  
  
  <script type="text/javascript" src="/vendors/fancybox/source/jquery.fancybox.pack.js"></script>
  <script type="text/javascript" src="/js/fancy-box.js?v=0.4.5.1"></script>


  <script type="text/javascript" src="/js/helpers.js?v=0.4.5.1"></script>
  

  <script type="text/javascript" src="/vendors/velocity/velocity.min.js"></script>
  <script type="text/javascript" src="/vendors/velocity/velocity.ui.min.js"></script>

  <script type="text/javascript" src="/js/motion_global.js?v=0.4.5.1" id="motion.global"></script>




  <script type="text/javascript" src="/js/nav-toggle.js?v=0.4.5.1"></script>
  <script type="text/javascript" src="/vendors/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  
<script type="text/javascript" src="/js/bootstrap.scrollspy.js?v=0.4.5.1" id="bootstrap.scrollspy.custom"></script>


<script type="text/javascript" id="sidebar.toc.highlight">
  $(document).ready(function () {
    var tocSelector = '.post-toc';
    var $tocSelector = $(tocSelector);
    var activeCurrentSelector = '.active-current';

    $tocSelector
      .on('activate.bs.scrollspy', function () {
        var $currentActiveElement = $(tocSelector + ' .active').last();

        removeCurrentActiveClass();
        $currentActiveElement.addClass('active-current');

        $tocSelector[0].scrollTop = $currentActiveElement.position().top;
      })
      .on('clear.bs.scrollspy', function () {
        removeCurrentActiveClass();
      });

    function removeCurrentActiveClass () {
      $(tocSelector + ' ' + activeCurrentSelector)
        .removeClass(activeCurrentSelector.substring(1));
    }

    function processTOC () {
      getTOCMaxHeight();
      toggleTOCOverflowIndicators();
    }

    function getTOCMaxHeight () {
      var height = $('.sidebar').height() -
                   $tocSelector.position().top -
                   $('.post-toc-indicator-bottom').height();

      $tocSelector.css('height', height);

      return height;
    }

    function toggleTOCOverflowIndicators () {
      tocOverflowIndicator(
        '.post-toc-indicator-top',
        $tocSelector.scrollTop() > 0 ? 'show' : 'hide'
      );

      tocOverflowIndicator(
        '.post-toc-indicator-bottom',
        $tocSelector.scrollTop() >= $tocSelector.find('ol').height() - $tocSelector.height() ? 'hide' : 'show'
      )
    }

    $(document).on('sidebar.motion.complete', function () {
      processTOC();
    });

    $('body').scrollspy({ target: tocSelector });
    $(window).on('resize', function () {
      if ( $('.sidebar').hasClass('sidebar-active') ) {
        processTOC();
      }
    });

    onScroll($tocSelector);

    function onScroll (element) {
      element.on('mousewheel DOMMouseScroll', function (event) {
          var oe = event.originalEvent;
          var delta = oe.wheelDelta || -oe.detail;

          this.scrollTop += ( delta < 0 ? 1 : -1 ) * 30;
          event.preventDefault();

          toggleTOCOverflowIndicators();
      });
    }

    function tocOverflowIndicator (indicator, action) {
      var $indicator = $(indicator);
      var opacity = action === 'show' ? 0.4 : 0;
      $indicator.velocity ?
        $indicator.velocity('stop').velocity({
          opacity: opacity
        }, { duration: 100 }) :
        $indicator.stop().animate({
          opacity: opacity
        }, 100);
    }

  });
</script>

<script type="text/javascript" id="sidebar.nav">
  $(document).ready(function () {
    var html = $('html');
    var TAB_ANIMATE_DURATION = 200;
    var hasVelocity = $.isFunction(html.velocity);

    $('.sidebar-nav li').on('click', function () {
      var item = $(this);
      var activeTabClassName = 'sidebar-nav-active';
      var activePanelClassName = 'sidebar-panel-active';
      if (item.hasClass(activeTabClassName)) {
        return;
      }

      var currentTarget = $('.' + activePanelClassName);
      var target = $('.' + item.data('target'));

      hasVelocity ?
        currentTarget.velocity('transition.slideUpOut', TAB_ANIMATE_DURATION, function () {
          target
            .velocity('stop')
            .velocity('transition.slideDownIn', TAB_ANIMATE_DURATION)
            .addClass(activePanelClassName);
        }) :
        currentTarget.animate({ opacity: 0 }, TAB_ANIMATE_DURATION, function () {
          currentTarget.hide();
          target
            .stop()
            .css({'opacity': 0, 'display': 'block'})
            .animate({ opacity: 1 }, TAB_ANIMATE_DURATION, function () {
              currentTarget.removeClass(activePanelClassName);
              target.addClass(activePanelClassName);
            });
        });

      item.siblings().removeClass(activeTabClassName);
      item.addClass(activeTabClassName);
    });

    $('.post-toc a').on('click', function (e) {
      e.preventDefault();
      var targetSelector = escapeSelector(this.getAttribute('href'));
      var offset = $(targetSelector).offset().top;
      hasVelocity ?
        html.velocity('stop').velocity('scroll', {
          offset: offset  + 'px',
          mobileHA: false
        }) :
        $('html, body').stop().animate({
          scrollTop: offset
        }, 500);
    });

    // Expand sidebar on post detail page by default, when post has a toc.
    var $tocContent = $('.post-toc-content');
    if (isDesktop() && CONFIG.sidebar === 'post') {
      if ($tocContent.length > 0 && $tocContent.html().trim().length > 0) {
        displaySidebar();
      }
    }
  });
</script>



  <script type="text/javascript">
    $(document).ready(function () {
      if (CONFIG.sidebar === 'always') {
        displaySidebar();
      }
      if (isMobile()) {
        FastClick.attach(document.body);
      }
    });
  </script>

  

  
  

  
  <script type="text/javascript" src="/js/lazyload.js"></script>
  <script type="text/javascript">
    $(function () {
      $("#posts").find('img').lazyload({
        placeholder: "/images/loading.gif",
        effect: "fadeIn"
      });
    });
  </script>

  <!-- google search, added by felix -->
  <script>
      $('#gg-form').on('submit', function(e) {
        var keyword = $.trim($(this).find('#gg-search-input').val());
        if (keyword) {
          location.href = 'https://www.google.com.hk/?gfe_rd=cr&ei=hXw8VpjtHuLC8AeSuIjQAg&gws_rd=ssl#safe=strict&q='+encodeURIComponent(keyword)+'+site:xgfe.github.io';
        }
        return false;
      });
  </script>
  <!-- baidu 站长自动推送 -->
  <script>
  (function(){
      var bp = document.createElement('script');
      bp.src = '//push.zhanzhang.baidu.com/push.js';
      var s = document.getElementsByTagName("script")[0];
      s.parentNode.insertBefore(bp, s);
  })();
  </script>
</body>
</html>
